package os

import (
	"encoding/json"
	"gitee.com/zhouhailin/hutool-go/hutool/io"
	"gitee.com/zhouhailin/hutool-go/hutool/time"
	"io/ioutil"
	"log"
	"os/exec"
	"strings"
)

type Command struct {
	Dir string
	Cmd string
}

var timeUtil time.TimeUtil
var ioUtil io.IoUtil

func init() {
	timeUtil = time.TimeUtil{}
	ioUtil = io.IoUtil{}
}

func map2string(m map[string]string) string {
	data, err := json.Marshal(m)
	if err != nil {
		return ""
	}
	return string(data)
}

func writeFile(cmd *Command) (string, error) {
	var sb strings.Builder
	sb.WriteString("#/bin/bash\n\n")
	sb.WriteString("export LANG=en_US.UTF-8\n\n")
	if cmd.Dir != "" {
		sb.WriteString("cd " + cmd.Dir + "\n\n")
	} else {
		sb.WriteString("cd /tmp \n\n")
		//sb.WriteString("cd " + util.GetWorkDir() + "\n\n")
	}
	sb.WriteString(cmd.Cmd)
	pathname := "/tmp/" + time.TimeUtil{}.NowMillisStr() + ".sh"
	//pathname := util.GetTempDir() + "/" + timeUtil.NowMillisStr() + ".sh"
	err := ioUtil.WriteText(pathname, sb.String())
	if err != nil {
		return "", err
	}
	return pathname, nil
}

func execBashFile(pathname string) string {
	m := map[string]string{}
	m["startTime"] = timeUtil.Now()
	m["stderr"] = ""
	m["stdout"] = ""

	cmd := exec.Command("/bin/sh", "-c", pathname)
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		m["stderr"] = err.Error()
		m["endTime"] = timeUtil.Now()
		return map2string(m)
	}
	stderr, err := cmd.StderrPipe()
	if err != nil {
		m["stderr"] = err.Error()
		m["endTime"] = timeUtil.Now()
		return map2string(m)
	}
	if err := cmd.Start(); err != nil {
		m["stderr"] = err.Error()
		m["endTime"] = timeUtil.Now()
		return map2string(m)
	}

	bytesErr, err := ioutil.ReadAll(stderr)
	if err != nil {
		m["stderr"] = err.Error()
		m["endTime"] = timeUtil.Now()
		return map2string(m)
	}

	if len(bytesErr) != 0 {
		m["stderr"] = string(bytesErr)
		m["endTime"] = timeUtil.Now()
		return map2string(m)
	}

	bytes, err := ioutil.ReadAll(stdout)
	if err != nil {
		m["stderr"] = err.Error()
		//fmt.Println("ReadAll stdout: ", err.Error())
		m["endTime"] = timeUtil.Now()
		return map2string(m)
	}

	if err := cmd.Wait(); err != nil {
		m["stderr"] = err.Error()
		//fmt.Println("Wait: ", err.Error())
		m["endTime"] = timeUtil.Now()
		return map2string(m)
	}
	m["stdout"] = string(bytes)
	m["endTime"] = timeUtil.Now()
	data, err := json.Marshal(m)
	if err != nil {
		return map2string(m)
	}
	return string(data)
}

func deleteFile(filename string) error {
	return ioUtil.DeleteFile(filename)
}

func Execute(cmd *Command) (string, error) {
	filename, err := writeFile(cmd)
	if err != nil {
		return "", err
	}
	defer func(filename string) {
		err := deleteFile(filename)
		if err != nil {
			log.Print(err)
		}
	}(filename)
	resp := execBashFile(filename)
	return resp, nil
}
